package com.luych.toolbox.docker.service.sdk.container;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.luych.toolbox.docker.service.sdk.container.model.*;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

public class DockerContainer {

    private final String url;
    private final String id;

    public DockerContainer(String url, String id) {
        this.url = url;
        this.id = id;
    }

    public DockerContainerInspectResult inspect() {
        final String uri = url + "/containers/{id}/json";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(uri, DockerContainerInspectResult.class, id);
    }

    public DockerContainerTopResult top() {
        final String uri = url + "/containers/{id}/top";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(uri, DockerContainerTopResult.class, id);
    }

    public List<String> log(long tail) {
        List<String> logs = new ArrayList<>();
        this.log(logs::add, tail, false);
        return logs;
    }

    public void log(Consumer<String> lineConsumer, long tail, Boolean follow) {
        final String uri = url + "/containers/{id}/logs" +
                "?timestamps=true&stdout=true&stderr=true&tail={1}&follow={2}";
        RestTemplate restTemplate = new RestTemplate();
        ResponseExtractor<?> responseExtractor = response -> {
            InputStreamReader inputStreamReader = new InputStreamReader(response.getBody());
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                lineConsumer.accept(line);
            }
            return Object.class;
        };
        RequestCallback requestCallback = request -> {
        };
        restTemplate.execute(uri, HttpMethod.GET, requestCallback, responseExtractor, id, tail, follow);
    }

    public DockerContainerChangesResult change() {
        final String uri = url + "/containers/{id}/changes";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(uri, DockerContainerChangesResult.class, id);
    }

    public <R> R export(ResponseExtractor<R> responseExtractor) {
        final String uri = url + "/containers/{id}/export";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.execute(uri, HttpMethod.GET, request -> {
        }, responseExtractor, id);
    }

    public void export(File file, Consumer<Long> downloaded) {
        final int bufferSize = 1024;
        ResponseExtractor<File> responseExtractor = response -> {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            InputStream inputStream = response.getBody();
            byte[] buffer = new byte[bufferSize];
            int length;
            long total = 0;
            while ((length = inputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, length);
                total += length;
                if (downloaded != null) {
                    downloaded.accept(total);
                }
            }
            return file;
        };
        this.export(responseExtractor);
    }

    public void export(File file) {
        this.export(file, null);
    }

    public DockerContainerStatsResult stats() {
        final String uri = url + "/containers/{id}/stats?stream=false";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(uri, DockerContainerStatsResult.class, id);
    }

    public void stats(Consumer<DockerContainerStatsResult> statConsumer) {
        final String uri = url + "/containers/{id}/stats";
        RestTemplate restTemplate = new RestTemplate();
        ResponseExtractor<?> responseExtractor = (ResponseExtractor<Object>) response -> {
            InputStream inputStream = response.getBody();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                DockerContainerStatsResult statsResult = objectMapper.readValue(line, DockerContainerStatsResult.class);
                statConsumer.accept(statsResult);
            }
            return Object.class;
        };
        restTemplate.execute(uri, HttpMethod.GET, request -> {
        }, responseExtractor, id);
    }

    public void start() {
        final String uri = url + "/containers/{id}/start";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id);
    }

    public void stop() {
        final String uri = url + "/containers/{id}/stop";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id);
    }

    public void restart() {
        final String uri = url + "/containers/{id}/restart";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id);
    }

    public void kill() {
        final String uri = url + "/containers/{id}/kill";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id);
    }

    public void pause() {
        final String uri = url + "/containers/{id}/pause";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id);
    }

    public void unpause() {
        final String uri = url + "/containers/{id}/unpause";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id);
    }

    public void rename(String name) {
        final String uri = url + "/containers/{id}/rename?name={1}";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id, name);
    }

    public void wait(String name) {
        final String uri = url + "/containers/{id}/wait";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject(uri, null, String.class, id, name);
    }

    public void remove() {
        final String uri = url + "/containers/{id}";
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.delete(uri, id);
    }

    public DockerContainerCommitResult commit(String repo, String tag) {
        final String uri = url + "/commit?container={1}&repo={2}&tag={3}";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.postForObject(uri, Collections.emptyMap(), DockerContainerCommitResult.class, id, repo, tag);
    }

    public DockerContainerUpdateResult update(DockerContainerUpdateParam param) {
        final String uri = url + "/containers/{id}/update";
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.postForObject(uri, param, DockerContainerUpdateResult.class, id);
    }
}
